package com.pp.demo.module.api.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import com.pp.demo.common.util.RedisUtil;
import com.pp.demo.module.api.entity.SeckillOrder;
import com.pp.demo.module.api.entity.SeckillActivity;
import com.pp.demo.module.api.service.OrderService;
import com.pp.demo.module.api.service.SeckillActivityService;
import com.pp.demo.module.api.service.SeckillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;

@Slf4j
@Service
public class SeckillServiceImpl implements SeckillService {

    private static final String STOCK_KEY_PREFIX = "seckill:stock:";

    @Autowired
    private SeckillActivityService activityService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public List<SeckillActivity> getActiveSeckillActivities() {
        log.info("列出所有有效活动");
        LocalDateTime now = LocalDateTime.now();
        return activityService.listValidActivities(true, now);
    }

    @Override
    public boolean checkStock(Long activityId) {
        log.info("检查库存");
        String stockKey = STOCK_KEY_PREFIX + activityId;
        Integer stock = (Integer) redisUtil.get(stockKey);
        return stock != null && stock > 0;
    }

    @Override
    @Transactional
    public SeckillOrder processSeckill(Long userId, Long activityId) {
        log.info("执行秒杀");
        String stockKey = STOCK_KEY_PREFIX + activityId;

        // 使用Redis原子操作扣减库存
        Boolean success = redisUtil.decr(stockKey, 1) >= 0;

        if (success) {
            // 扣减数据库库存
            int rows = activityService.decreaseStock(activityId);
            if (rows > 0) {
                // 创建订单
                SeckillActivity activity = activityService.getById(activityId);
                if (activity != null) {
                    SeckillOrder seckillOrder = new SeckillOrder();
                    seckillOrder.setUserId(userId);
                    seckillOrder.setProductId(activity.getProductId());
                    seckillOrder.setSeckillActivityId(activityId);
                    seckillOrder.setPayment(activity.getSeckillPrice());
                    seckillOrder.setStatus(1); // 1表示待支付
                    seckillOrder.setCreateTime(LocalDateTimeUtil.now());

                    orderService.save(seckillOrder);
                    return seckillOrder;
                }
            } else {
                // 数据库扣减失败，回滚Redis库存
                redisUtil.incr(stockKey, 1);
            }
        }

        return null; // 秒杀失败
    }

    @Override
    public String getSeckillResult(Long orderId) {
        log.info("获取秒杀结果");
        SeckillOrder seckillOrder = orderService.getById(orderId);
        if (seckillOrder == null) {
            return "秒杀失败";
        } else if (seckillOrder.getStatus() == 1) {
            return "秒杀成功，请尽快支付";
        } else {
            return "订单已处理：" + seckillOrder.getStatus();
        }
    }

    // 定时任务：将秒杀活动库存加载到Redis
//    @Async
    // 定时任务由 Spring 内置的单线程调度器（默认是ThreadPoolTaskScheduler，核心线程数为 1）执行，所有定时任务会在同一个线程中串行执行
    // 如果定时任务执行很快（比如毫秒级完成）：
    // 即使不配置@Async，也不会影响 Tomcat 处理正常请求。因为定时任务线程只是短暂运行，不会长期占用资源。
    // 如果定时任务执行很慢（比如秒级甚至分钟级）：
    // 此时会影响其他定时任务的执行（因为单线程串行），但不会直接影响 Tomcat 的 HTTP 请求处理。
    @Scheduled(fixedRate = 15 * 60 * 1000) // 每分钟执行一次
    public void loadSeckillStockToRedis() {
        log.info("[定时任务]: 将秒杀活动库存加载到Redis");
        LocalDateTime now = LocalDateTime.now();
        List<SeckillActivity> activities = activityService.listValidActivities(true, now);

        for (SeckillActivity activity : activities) {
            String stockKey = STOCK_KEY_PREFIX + activity.getId();
            long expireSeconds = now.until(activity.getEndTime(), ChronoUnit.SECONDS);
            redisUtil.setIfAbsent(stockKey, activity.getStock(), expireSeconds);
        }
    }
}
